

local M = {}

function M.mask_data(data, mask)
    local t = {}
    for i = 1, #data do
        local m = string.byte(mask, ((i - 1) % 4) + 1)
        local b = string.byte(data, i) ~ m
        table.insert(t, string.char(b))
    end
    return table.concat(t, "")
end

function M.random_mask()
    local t = {}
    for i = 1, 4 do
        table.insert(t, math.random(1, 255))
    end
    return string.char(table.unpack(t))
end

function M.make_packet(data, mask, is_text)
    local buf = {}
    local v1 = 1 << 7 -- FIN
    v1 = v1 | (is_text and 0x1 or 0x2)
    local len = #data
    local v2 = 0
    if len > 65535 then
        v2 = 127
    elseif len > 125 and len <= 65535 then
        v2 = 126
    else
        v2 = len
    end
    local is_mask = mask and (1 << 7) or 0
    table.insert(buf, string.pack("BB", v1, v2 | is_mask))
    if v2 == 127 then
        table.insert(buf, string.pack(">I8", len))
    elseif v2 == 126 then
        table.insert(buf, string.pack(">I2", len))
    end

    if mask then
        local mask = M.random_mask()
        table.insert(buf, mask)
        data = M.mask_data(data, mask)
    end
    table.insert(buf, data)
    return table.concat(buf, "")
end


function M.read_packet(channel)
    local head = channel.read(2)
    assert(head, "closed")

    local h = string.unpack(">I2", head)
    local opcode = (h & 0xf00) >> 8

    if opcode == 0x8 then
        error("closed")
    end

    local mask = h & (1 << 7)
    local len = h & 0x7f
    if len <= 125 then
    elseif len == 126 then
        local rd = channel.read(2)
        len = string.unpack(">I2", rd)
    elseif len == 127 then
        local rd = channel.read(8)
        len = string.unpack(">I8", rd)
    end
    local mask_data
    if mask > 0 then
        mask_data = channel.read(4)
        assert(head, "closed")
    end
    local body = channel.read(len)
    assert(body, "closed")
    if mask > 0 then
        local t = {}
        local body_len = #body
        for i = 1, body_len do
            local m = string.byte(mask_data, ((i - 1) % 4) + 1)
            local b = string.byte(body, i) ~ m
            table.insert(t, string.char(b))
        end
        body = table.concat(t, "")
    end
    return body
end

return M
